package com.laamella.syntherface.synths.models;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * Maps derived parameters to a single concrete parameter, and keeps them
 * synchronized.
 */
public class MappedDerivedParameterMapper {
	private final int[][] mapping;
	private MappedDerivedParameterModel[] derivedParameters;
	private ParameterModel concreteParameter;
	private static final int ConcreteParameterColumn = 0;
	private final int derivedParameterCount;
	private int currentRow = -1;

	private static class DerivedParameterChangeListener implements
			ChangeListener {
		private final int derivedParameterNumber;
		private final MappedDerivedParameterMapper mapper;

		public DerivedParameterChangeListener(
				MappedDerivedParameterMapper mapper, int derivedParameterNumber) {
			this.derivedParameterNumber = derivedParameterNumber;
			this.mapper = mapper;
		}

		@Override
		public void stateChanged(ChangeEvent e) {
			mapper
					.recalculateAfterDerivedParameterChanged(derivedParameterNumber);
		}
	}

	public MappedDerivedParameterMapper(int[][] mapping,
			ParameterModel concreteParameter,
			MappedDerivedParameterModel... derivedParameters) {
		this.mapping = mapping;
		this.derivedParameterCount = mapping[0].length - 1;
		this.derivedParameters = derivedParameters;
		for (int derivedParameterNumber = 0; derivedParameterNumber < derivedParameters.length; derivedParameterNumber++) {
			derivedParameters[derivedParameterNumber]
					.addChangeListener(new DerivedParameterChangeListener(
							this, derivedParameterNumber));
		}
		this.concreteParameter = concreteParameter;
		this.concreteParameter.addChangeListener(new ChangeListener() {
			@Override
			public void stateChanged(ChangeEvent arg0) {
				recalculateAfterConcreteParameterChanged();
			}
		});
	}

	public void recalculateAfterDerivedParameterChanged(
			int derivedParameterNumber) {
		// find new matching row in the derived part of the mapping;
		for (int row = 0; row < mapping.length; row++) {
			boolean correctRow = true;
			for (int parameter = 0; parameter < derivedParameterCount; parameter++) {
				System.out.println("Comparing "+mapping[row][parameter + 1] +" and "+derivedParameters[parameter].getValue());
				if (mapping[row][parameter + 1] != derivedParameters[parameter]
						.getValue()) {
					correctRow = false;
				}
			}
			if (correctRow) {
				currentRow = row;
				System.out.println("Correct row is " + row);
				break;
			}else{
				System.out.println("not the right row");
			}
		}

		// adjust concrete parameter;
		if(currentRow!=-1){
			concreteParameter.setValue(mapping[currentRow][ConcreteParameterColumn]);
		}
	}

	public void recalculateAfterConcreteParameterChanged() {
		// find new matching row in the concrete parameter value column of the
		// mapping;
		for (int row = 0; row < mapping.length; row++) {
			if (mapping[row][ConcreteParameterColumn] == concreteParameter
					.getValue()) {
				currentRow = row;
				System.out.println("Correct row is " + row);
				break;
			}
		}
		// adjust derived parameters;
		if (currentRow != -1) {
			for (int derivedParameter = 0; derivedParameter < derivedParameterCount; derivedParameter++) {
				derivedParameters[derivedParameter]
						.setValue(mapping[currentRow][derivedParameter + 1]);
			}
		}
	}

}
